﻿using System;
using System.Collections.Generic;
using net.entity.framework.cache;
using net.entity.framework.db;
using net.entity.framework.query;

namespace net.entity.framework {
    /// <summary>
    /// This is a light-weighted reference to the Entity. It enables On-demand load of Entity. 
    /// </summary>
    public abstract class EntityRef {
        #region Memebrs

        protected readonly object syncLock = new object();

        private readonly int id;
        private readonly int typeId;
        private readonly int revision;

        private readonly QueryContext queryContext;

        #endregion

        #region Properties

        public int Id {
            get { return id; }
        }

        public int TypeId {
            get { return typeId; }
        }

        public int Revision {
            get { return revision; }
        }

        protected Entity Target { get; set; }

        public Type ObjectType {
            get { return TypeIdCache.GetValue(typeId).EntityType; }
        }

        public bool IsUnspecified {
            get { return id < 0; }
        }

        #endregion

        #region Constructors

        protected EntityRef(int id, int typeId, int revision) {
            this.id = id;
            this.typeId = typeId;
            this.revision = revision;
            queryContext = null;
        }

        protected EntityRef(int id, int typeId, int revision, QueryContext queryContext) {
            this.id = id;
            this.typeId = typeId;
            this.revision = revision;
            this.queryContext = queryContext;
        }

        #endregion

        #region Methods

        internal static EqlEntityRef GetQueryRoot<T>() where T : EntityRef {
            return EqlEntityRefCache.LookupOrCreate<T>().EqlEntityRef;
        }

        /// <summary>
        /// Resolve EqlEntity
        /// 1. Lookup EntityCache
        /// 2. Run Prefetcher, if not found in EntityCache
        /// </summary>
        protected TEntity Resolve<TEntity>()
            where TEntity : Entity {
            var key = EntityKey.Create(this);
            TEntity value;
            if (EntityCache.Instance.TryGetValue(key, out value)) {
                return value;
            }
            //Call prefetcher
            Prefetcher.RunAndCache(key, queryContext);
            return EntityCache.Instance.GetValue<TEntity>(key);
        }

        public static IEnumerable<T> Find<T>(QueryClause whereClause, params Terminal[] orderBy) where T : EntityRef {
            return DbRepository.Instance.Find<T>(whereClause, orderBy);
        }

        public static T FindFirst<T>(QueryClause whereClause, bool strict) where T : EntityRef {
            return DbRepository.Instance.FindFirst<T>(whereClause, strict);
        }

        public static T FindFirst<T>(int id, bool strict) where T : EntityRef {
            return FindFirst<T>(id, 0, strict);
        }

        public static T FindFirst<T>(int id, int revision, bool strict) where T : EntityRef {
            return DbRepository.Instance.FindFirst<T>(id, revision, strict);
        }

        public static int Delete<T>(QueryClause whereClause) where T : EntityRef {
            return DbRepository.Instance.Delete<T>(whereClause);
        }

        #endregion
    }
}